<!--
 * @Author: liucong19
 * @Date: 2021-07-16 19:16:08
 * @LastEditTime: 2021-07-22 10:05:50
 * @LastEditors: liucong19
 * @Description: 基于 MSE 播放视频流
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>基于 MSE 播放视频流</title>
  <style>
    .video-container {display: flex; justify-content: center; align-items: center;}
  </style>
</head>
<body>
  <div class="video-container">
    <video id="video" width="800" height="450" controls></video>
  </div>

  <script type="module">
    const video = document.querySelector('#video')
    const videoCodec = 'video/mp4; codecs="avc1.42E01E"'
    const audioCodec = 'audio/mp4; codecs="mp4a.40.2"'

    // 判断浏览器是否支持 MediaSource
    const isSupportMediaSource = window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function' && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42c01f,mp4a.40.2"')
    if (isSupportMediaSource) {
      // 创建实例
      let mediaSource = new MediaSource()
      video.src = URL.createObjectURL(mediaSource)

      // 在给 video.src 赋值之后触发
      mediaSource.addEventListener('sourceopen', sourceOpen)

      function sourceOpen() {
        videoBuffer = mediaSource.addSourceBuffer(videoCodec)
        audioBuffer = mediaSource.addSourceBuffer(audioCodec)

        videoBuffer.addEventListener('updateend', () => {
          mediaSource.endOfStream()
          video.play()
        })

        // 通过 websocket 获取原始视频流，处理后通过 SourceBuffer 喂给 mediaSource
        const ws = new WebSocket('wss://xxx.websocket.com')
        
        ws.onopen = function(evt) {  
          console.log('Connection open ...')
          ws.send('fetch Data')
        }
        
        ws.onmessage = function(evt) {
          // 可以在灌入数据前进行转码等操作 
          sourceBuffer.appendBuffer(evt.data); 
        }
        
        ws.onclose = function(evt) {
          console.log('Connection closed.')
        }
      }

      // 主动调用终止、视频数据解析、播放错误时被触发
      mediaSource.addEventListener('sourceended', () => { 
        // TODO 
      })

      // 在 SourceBuffer 和 MediaElement中无可用数据(一般是播放到视频末尾)时被触发
      mediaSource.addEventListener('sourceclose', () => { 
        // TODO 
      })
    }
  </script>
</body>
</html>